package com.person.mengshuai.mytestdemo.util.excel;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.person.mengshuai.mytestdemo.annotation.ExcelColumnName;
import com.person.mengshuai.mytestdemo.exception.base.BusinessException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * excel工具类
 *
 * @author 孟帅
 * @since 2024/7/3
 */
public class MyExcelUtil {
    public static final Logger LOGGER = LoggerFactory.getLogger(MyExcelUtil.class);

    public static <T> List<Object> getColumnNameList(T entity) {
        List<Object> columnNameList = new ArrayList<>();
        Class<? extends Object> bean = entity.getClass();
        Field[] declaredFields = bean.getDeclaredFields();
        for (Field field : declaredFields) {
            try {
                if (!"serialVersionUID".equals(field.getName())) {
                    String name = field.getName();
                    ExcelColumnName annotation = field.getAnnotation(ExcelColumnName.class);
                    if (StringUtils.isNotEmpty(annotation.name())) {
                        name = annotation.name();
                    }
                    columnNameList.add(name);
                }
            } catch (Exception e) {
                LOGGER.error("the method getColumnNameList error:{}", e.getMessage());
            }
        }
        return columnNameList;
    }

    public static <T> List<List<Object>> getDataContentList(List<T> entityList) {
        List<List<Object>> dataContentList = new ArrayList<>();
        if (CollectionUtils.isEmpty(entityList)) {
            return dataContentList;
        }
        List<Object> columnNameList = getColumnNameList(entityList.get(0));
        dataContentList.add(columnNameList);
        for (T t : entityList) {
            List<Object> dataContent = new ArrayList<>();
            dataContentList.add(dataContent);
            Class<?> bean = t.getClass();
            Field[] declaredFields = bean.getDeclaredFields();
            for (Field field : declaredFields) {
                if (!"serialVersionUID".equals(field.getName())) {
                    if (!field.isAccessible()) {
                        field.setAccessible(true);
                    }
                    try {
                        dataContent.add(field.get(t));
                    } catch (IllegalAccessException e) {
                        LOGGER.error("the method getDataContent IllegalAccessException:{}", e.getMessage());
                    }
                }
            }
        }
        return dataContentList;
    }

    public static void dataExport(String fileName, String sheetName, String titleName, List<List<Object>> data,
                                  HttpServletRequest request,
                                  HttpServletResponse response) throws BusinessException, IOException {
        XSSFWorkbook xssfWorkbook = getXssfWorkbook(sheetName, titleName, data);
        responseToWeb(fileName, request, response, xssfWorkbook);
    }

    public static void responseToWeb(String fileName, HttpServletRequest request, HttpServletResponse response,
                                     XSSFWorkbook xssfWorkbook) throws IOException, BusinessException {
        String agent = request.getHeader("USER-AGENT").toLowerCase();
        if (agent.contains("firefox")) {
            fileName =
                    "=?UTF-8?B?" + (Base64Utils.encodeToString(fileName.getBytes(StandardCharsets.UTF_8))) + "?=";
        } else {
            fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            fileName = fileName.replace("+", "%20");
        }
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
        ByteArrayOutputStream baos = null;
        ByteArrayInputStream bais = null;
        OutputStream os = response.getOutputStream();
        try {
            baos = new ByteArrayOutputStream();
            xssfWorkbook.write(baos);
            bais = new ByteArrayInputStream(baos.toByteArray());
            response.setHeader("Content-Length", String.valueOf(bais.available()));
            byte[] b = new byte[1024];
            while ((bais.read(b)) > 0) {
                os.write(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(bais);
            os.flush();
            IOUtils.closeQuietly(os);
        }
    }

    public static void setCellFormula(XSSFCell titleCell, String formula) {
        titleCell.setCellFormula(formula);
    }

    public static void evaluateFormulaCell(XSSFWorkbook xssfWorkbook, XSSFCell cellResult) {
        // 如果需要，可以强制重新计算公式
        FormulaEvaluator evaluator = xssfWorkbook.getCreationHelper().createFormulaEvaluator();
        evaluator.evaluateFormulaCell(cellResult);
    }

    public static XSSFWorkbook getXssfWorkbook(String sheetName, String titleName, List<List<Object>> data) {
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
        XSSFSheet sheet = xssfWorkbook.createSheet(sheetName);
        if (CollectionUtil.isNotEmpty(data)) {
            int size = data.size();
            int width = data.get(0).size();
            XSSFRow titleRow = sheet.createRow(0);
            XSSFCell titleCell = titleRow.createCell(0);
            CellRangeAddress cellAddresses = new CellRangeAddress(0, 0, 0, width - 1);
            sheet.addMergedRegion(cellAddresses);
            titleCell.setCellValue(titleName);
            for (int rowIndex = 1; rowIndex <= size; rowIndex++) {
                List<Object> rowValue = data.get(rowIndex - 1);
                XSSFRow row = sheet.createRow(rowIndex);
                for (int columnIndex = 0; columnIndex < width; columnIndex++) {
                    Object cellValue = rowValue.get(columnIndex);
                    XSSFCell cell = row.createCell(columnIndex);
                    setCellValue(cell, cellValue);
                }
            }
        }
        return xssfWorkbook;
    }


    private static void setResponseAttribute(HttpServletRequest request, HttpServletResponse response,
                                             String fileName) throws UnsupportedEncodingException, BusinessException {
        if (!fileName.endsWith(".xlsx")) {
            throw new BusinessException("文件名称格式不对");
        }
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        String agent = request.getHeader("USER-AGENT").toLowerCase();
        String encodeFileName = "";
        if (agent.contains("firefox")) {
            encodeFileName =
                    "=?UTF-8?B?" + (Base64Utils.encodeToString(fileName.getBytes(StandardCharsets.UTF_8))) + "?=";
        } else {
            encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            encodeFileName = encodeFileName.replace("+", "%20");
        }
        response.setHeader("Content-disposition", "attachment; filename=" + encodeFileName);
    }

    public static void setCellValue(XSSFCell cell, Object value) {
        if (value == null || "".equals(value.toString())) {
            return;
        }
        if (value instanceof Integer) {
            cell.setCellValue((Integer) value);
        } else if (value instanceof Float) {
            cell.setCellValue((Float) value);
        } else if (value instanceof String) {
            cell.setCellValue(value.toString());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof Date) {
            Date date = (Date) value;
            cell.setCellValue(DateUtil.formatDateTime(date));
        }
    }


}
